package uk.ac.cam.djjb2.mygame;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public abstract class CancellableTask<T> implements RunnableFuture<T> {
	private volatile boolean cancelling;
	private volatile boolean cancelled;
	private volatile boolean done;
	private CountDownLatch latch;
	private volatile T result;
	private volatile ExecutionException exception;
	
	protected CancellableTask() {
		latch = new CountDownLatch(1);
		cancelling = false;
		cancelled = false;
		done = false;
		result = null;
		exception = null;
	}
	
	protected boolean isCancelling() {
		return cancelling;
	}
	
	@Override
	public synchronized boolean cancel(boolean paramBoolean) {
		
		if (!cancelling) {
			cancelling = true;
		}
		
		try {
			latch.await();
		} catch (InterruptedException e) {
			return false;
		}
		
		cancelled = true;
		result = null;
		
		return true;
	}

	@Override
	public boolean isCancelled() {
		return done && cancelled;
	}

	@Override
	public boolean isDone() {
		return done;
	}

	@Override
	public synchronized T get() throws InterruptedException, ExecutionException {
		latch.await();
		
		if (exception != null)
			throw exception;
		
		return result;
	}

	@Override
	public synchronized T get(long paramLong, TimeUnit paramTimeUnit)
			throws InterruptedException, ExecutionException, TimeoutException {
		if (!latch.await(paramLong, paramTimeUnit))
			return null;
		
		if (exception != null)
			throw exception;
		
		return result;
	}
	
	protected abstract T execute() throws Throwable;

	@Override
	public void run() {
		try {
			result = execute();
		} catch (Throwable e) {
			exception = new ExecutionException(e);
		}
		if (cancelling)
			cancelled = true;
		done = true;
		latch.countDown();
	}

}
